﻿Option Explicit On ' Make sure all items are explicitly declared
Option Strict On ' Be Very Strict about things here!
Imports System.Net
Namespace ZuneCardr
    ''' <summary>Social</summary>
    ''' <author>Comentsys</author>
    ''' <version>4.0.0</version>
    ''' <created>27th January 2009</created>
    ''' <modified>23rd February 2009</modified>
    ''' <remarks>Zune Social Data</remarks>
    Public Class Social

        ' Private Constants

        Private ReadOnly ZCARD_REG As New System.Text.RegularExpressions.Regex("[a-zA-Z0-9_\s]")  ' Valid Zune Tag
        Private Const ZCARD_TAG_MAXLENGTH As Integer = 16 ' Tag Length
        Private Const ZCARD_URL As String = "http://social.zune.net/zcard/usercardservice.ashx" ' Service URL
        Private Const ZCARD_HOST As String = "zune.net" ' Zune Website
        Private Const ZCARD_PARAM As String = "zunetag" ' Service Parameter
        Private Const ZCARD_WEBLINK As String = "http://www.zune.net"
        ' Errors
        Private Const ZCARD_ERR_DOWNLOAD As String = "Download incomplete or service unavailable"
        Private Const ZCARD_ERR_PROCESS As String = "Unable to process Zune Card"
        Private Const ZCARD_ERR_INVALID As String = "Enter valid Zune Tag"
        Private Const ZCARD_ERR_PLAYLIST As String = "Playlist information unavailable"
        ' XML
        Private Const TAG_ROOT As String = "zCard"
        Private Const ATTR_FORMAT As String = "format"
        Private Const VAL_TILEBIG As String = "tileBig"
        Private Const VAL_TILESMALL As String = "tileSmall"
        Private Const VAL_BGLARGE As String = "backgroundLarge"
        Private Const VAL_BGSMALL As String = "backgroundSmall"
        ' General
        Private Const TEXT_BY As String = " by "

        ' Private Members

        Private strError As String ' Error Text
        Private strException As String ' Long Error Text
        Private strMessage As String = "" ' System Message
        Private xmlElement As XElement ' XML
        ' Class Members
        Private strURL As String
        Private strID As String
        Private strTag As String
        Private strStatus As String
        Private strName As String
        Private strLocation As String
        Private strBiography As String
        Private strPlays As String
        Private strTileLarge As String
        Private imgTileLarge As New BitmapImage
        Private strTileSmall As String
        Private imgTileSmall As New BitmapImage
        Private strBackgroundLarge As String
        Private imgBackgroundLarge As New BitmapImage
        Private strBackgroundSmall As String
        Private imgBackgroundSmall As New BitmapImage
        Private _Badges As New List(Of Badge) ' Badges
        Private _Destinations As New List(Of Destination) ' Destinations
        Private _Playlists As New List(Of Playlist) ' Playlists

        ' Public Enums

        Public Enum ImageVersion
            Small = 0
            Large = 1
        End Enum

        ' Public Events

        Public Event Downloaded(ByVal Success As Boolean)
        Public Event Processed(ByVal Success As Boolean)

        ' Private Methods

        ''' <summary>IsValidTag</summary>
        ''' <param name="ZuneTag">ZuneTag</param>
        ''' <returns>IsValidTag as Boolean</returns>
        ''' <remarks>Checks Zune Tag Characters and Length are Valid</remarks>
        Private Function IsValidTag(ByRef ZuneTag As String) As Boolean
            Return (Len(ZuneTag) > 0 _
                    And ZCARD_REG.Match(ZuneTag).Success _
                    And Len(ZuneTag) < ZCARD_TAG_MAXLENGTH)
        End Function

        ''' <summary>GetCardURL</summary>
        ''' <param name="ZuneTag">ZuneTag</param>
        ''' <returns>GetCardURL as String</returns>
        ''' <remarks>Gets the URL of a ZuneCard</remarks>
        Private Function GetCardURL(ByRef ZuneTag As String) As String
            Return ZCARD_URL & "?" & ZCARD_PARAM & "=" & _
            Uri.EscapeUriString(Trim(ZuneTag)) ' Encode for Spaces
        End Function

        ''' <summary>GetBadges</summary>
        ''' <param name="Elements">Badge XML Elements</param>
        Private Sub GetBadges(ByRef Elements As IEnumerable(Of XElement))
            _Badges.Clear()
            For Each Result In Elements
                _Badges.Add(New Badge(Result))
            Next
        End Sub

        ''' <summary>GetDestinations</summary>
        ''' <param name="Elements">Destination XML Elements</param>
        Private Sub GetDestinations(ByRef Elements As IEnumerable(Of XElement))
            _Destinations.Clear()
            For Each Result In Elements
                _Destinations.Add(New Destination(Result))
            Next
        End Sub

        ''' <summary>GetPlaylists</summary>
        ''' <param name="Elements">Playlist XML Elements</param>
        Private Sub GetPlaylists(ByRef Elements As IEnumerable(Of XElement))
            _Playlists.Clear()
            For Each Result In Elements
                _Playlists.Add(New Playlist(Result))
            Next
        End Sub

        ''' <summary>Process</summary>
        ''' <param name="Sender">Client</param>
        ''' <param name="e">Download Event</param>
        ''' <remarks>Processes Zune File once Downloaded</remarks>
        Private Sub Process(ByVal Sender As Object, _
                            ByVal e As DownloadStringCompletedEventArgs)
            Try
                If e.Cancelled Then
                    Throw New Exception(ZCARD_ERR_DOWNLOAD) ' Download Error
                    RaiseEvent Downloaded(False) ' Download Event Failure
                Else
                    RaiseEvent Downloaded(True) ' Download Event Success
                    xmlElement = XElement.Parse(e.Result) ' Load Data
                    If xmlElement.Name.LocalName = TAG_ROOT Then ' Process Data
                        Dim ImageDict As New Dictionary(Of String, String)
                        strMessage = Trim(xmlElement.<error>.<label>.Value) ' System Message
                        strURL = Trim(xmlElement.<user>.@uri)
                        strID = Trim(xmlElement.<user>.<id>.Value)
                        strTag = Trim(xmlElement.<user>.<label>.Value)
                        strStatus = Trim(xmlElement.<user>.<status>.Value)
                        ImageDict = GetImages(xmlElement.<user>.<image>.<url>)
                        If ImageDict.ContainsKey(VAL_TILESMALL) Then
                            strTileSmall = ImageDict.Item(VAL_TILESMALL)
                            imgTileSmall = New BitmapImage(New Uri(strTileSmall))
                        End If
                        If ImageDict.ContainsKey(VAL_TILEBIG) Then
                            strTileLarge = ImageDict.Item(VAL_TILEBIG)
                            imgTileLarge = New BitmapImage(New Uri(strTileLarge))
                        End If
                        strName = Trim(xmlElement.<user>.<manifest>.<userData>.<name>.Value)
                        strLocation = Trim(xmlElement.<user>.<manifest>.<userData>.<location>.Value)
                        strBiography = Trim(xmlElement.<user>.<manifest>.<userData>.<bio>.Value)
                        ImageDict = GetImages(xmlElement.<user>.<manifest>.<userData>.<image>.<url>)
                        If ImageDict.ContainsKey(VAL_BGSMALL) Then
                            strBackgroundSmall = ImageDict.Item(VAL_BGSMALL)
                            imgBackgroundSmall = New BitmapImage(New Uri(strBackgroundSmall))
                        End If
                        If ImageDict.ContainsKey(VAL_BGLARGE) Then
                            strBackgroundLarge = ImageDict.Item(VAL_BGLARGE)
                            imgBackgroundLarge = New BitmapImage(New Uri(strBackgroundLarge))
                        End If
                        strPlays = Trim(xmlElement.<user>.<manifest>.<userData>.<totalPlays>.Value)
                        GetBadges(xmlElement.<user>.<manifest>.<userData>.<badges>.<badge>)
                        GetDestinations(xmlElement.<user>.<manifest>.<destinations>.<destination>)
                        GetPlaylists(xmlElement.<user>.<manifest>.<playlists>.<playlist>)
                        RaiseEvent Processed(True) ' Process Success
                    Else
                        Throw New Exception(ZCARD_ERR_PROCESS) ' Process Error
                        RaiseEvent Processed(False) ' Process Failure
                    End If
                    End If
            Catch ex As Exception
                ErrorText = ex.Message ' Error Text
                ExceptionText = ex.ToString ' Full Error
                If ex.Message = ZCARD_ERR_PROCESS Then ' Determine Error Type
                    RaiseEvent Processed(False) ' Process Failed
                Else
                    RaiseEvent Downloaded(False) ' Download Failed
                End If
            End Try
        End Sub

        ' Shared Methods

        ''' <summary>GetImages</summary>
        ''' <param name="Elements">XML Elements Path</param>
        ''' <returns>Dictionary of Images</returns>
        ''' <remarks>Gets Large and Small Images</remarks>
        Shared Function GetImages(ByRef Elements As IEnumerable(Of XElement)) _
        As Dictionary(Of String, String)
            Dim Entries As New Dictionary(Of String, String)
            For Each Result In Elements
                Entries.Add(Result.Parent.Attribute(ATTR_FORMAT).Value, _
                            Result.Value)
            Next
            Return Entries
            Entries = Nothing
        End Function

        ' Public Methods

        ''' <summary>Constructor</summary>
        Public Sub New()
            MyBase.New()
        End Sub

        ''' <summary>Constructor</summary>
        ''' <param name="Tag">Zune Tag</param>
        Public Sub New(ByRef Tag As String)
            Download(Tag)
        End Sub

        ''' <summary>Download</summary>
        ''' <param name="Tag">ZuneTag</param>
        ''' <returns>True for Success, False for Fail</returns>
        ''' <remarks>Download Zune Card Information</remarks>
        Public Function Download(ByRef Tag As String) As Boolean
            Dim strZuneTag As String = Tag.Trim ' Remove Spaces
            Try
                If Not IsValidTag(strZuneTag) Then
                    Throw New Exception(ZCARD_ERR_INVALID) ' Invalid Tag Error
                    Return False ' Return False on Fail to Start Download
                End If
                Dim Client As WebClient
                Client = New WebClient ' Instance Client
                AddHandler Client.DownloadStringCompleted, AddressOf Process ' After Download
                Client.DownloadStringAsync(New Uri(GetCardURL(strZuneTag)))
                Return True
            Catch ex As Exception
                ErrorText = ex.Message ' Set Error Text
                ExceptionText = ex.ToString ' Full Error
                Return False ' Return False as Error Occurred
            End Try
        End Function

        ' Public Properties

        ''' <summary>User  URL</summary>
        ''' <returns>Zune Card URL</returns>
        Public ReadOnly Property URL() As String
            Get
                Return strURL
            End Get
        End Property

        ''' <summary>ID</summary>
        ''' <returns>Zune ID</returns>
        Public ReadOnly Property ID() As String
            Get
                Return strID
            End Get
        End Property

        ''' <summary>Tag</summary>
        ''' <returns>Zune Tag Text</returns>
        Public ReadOnly Property Tag() As String
            Get
                Return strTag
            End Get
        End Property

        ''' <summary>Tag</summary>
        ''' <returns>Status Text</returns>
        Public ReadOnly Property Status() As String
            Get
                Return strStatus
            End Get
        End Property

        ''' <summary>Avatar</summary>
        ''' <param name="Version">ImageVersion</param>
        ''' <returns>Avatar Image</returns>
        Public ReadOnly Property Avatar(ByVal Version As ImageVersion) As BitmapImage
            Get
                Select Case Version
                    Case ImageVersion.Large
                        Return imgTileLarge
                    Case Else
                        Return imgTileSmall
                End Select
            End Get
        End Property

        ''' <summary>Name</summary>
        ''' <returns>Name Text</returns>
        Public ReadOnly Property Name() As String
            Get
                Return strName
            End Get
        End Property

        ''' <summary>Location</summary>
        ''' <returns>Location Text</returns>
        Public ReadOnly Property Location() As String
            Get
                Return strLocation
            End Get
        End Property

        ''' <summary>Biography</summary>
        ''' <returns>Biography Text</returns>
        Public ReadOnly Property Biography() As String
            Get
                Return strBiography
            End Get
        End Property

        ''' <summary>Background</summary>
        ''' <param name="Version">ImageVersion</param>
        ''' <returns>Background Image</returns>
        Public ReadOnly Property Background(ByVal Version As ImageVersion) As BitmapImage
            Get
                Select Case Version
                    Case ImageVersion.Large
                        Return imgBackgroundLarge
                    Case Else
                        Return imgBackgroundSmall
                End Select
            End Get
        End Property

        ''' <summary>Plays</summary>
        ''' <param name="Numeric">Display Number Only if True</param>
        ''' <returns>Plays, Numeric only if True</returns>
        Public ReadOnly Property Plays(Optional ByVal Numeric As Boolean = False) As String
            Get
                'If strPlays = "" Then strPlays = "(0 Plays)"
                If Numeric Then ' Return Number Only (Preserve Comma)
                    Return String.Join(Nothing, System.Text.RegularExpressions.Regex.Split(strPlays, "[^\d,]"))
                Else ' Return Full Value
                    Return strPlays
                End If
            End Get
        End Property

        ''' <summary>LastPlayed</summary>
        ''' <returns>Last Played Value</returns>
        Public ReadOnly Property LastPlayed() As String
            Get
                For Each Item As Playlist In Playlists
                    If Item.Type = Playlist.RECENT And Item.Tracks.Count > 0 Then
                        Return Item.Tracks.Item(0).Name & TEXT_BY & _
                            Item.Tracks.Item(0).Album.Artist.Name
                    End If
                Next
                Return ""
            End Get
        End Property

        ''' <summary>Weblink</summary>
        ''' <returns>Zune Social Weblink</returns>
        Public ReadOnly Property Weblink() As String
            Get
                For Each Item As Destination In Destinations
                    If Item.Type = Destination.HOME Then
                        Return Item.URL
                    End If
                Next
                Return ZCARD_WEBLINK
            End Get
        End Property

        ''' <summary>SendURL</summary>
        ''' <returns>Zune Send Card Weblink</returns>
        Public ReadOnly Property SendURL() As String
            Get
                For Each Item As Destination In Destinations
                    If Item.Type = Destination.SEND_CARD Then
                        Return Item.URL
                    End If
                Next
                Return ZCARD_WEBLINK
            End Get
        End Property

        ''' <summary>MessageURL</summary>
        ''' <returns>Zune Message Weblink</returns>
        Public ReadOnly Property MessageURL() As String
            Get
                For Each Item As Destination In Destinations
                    If Item.Type = Destination.SEND_MSG Then
                        Return Item.URL
                    End If
                Next
                Return ZCARD_WEBLINK
            End Get
        End Property

        ''' <summary>Badges</summary>
        ''' <returns>List of Badges</returns>
        Public ReadOnly Property Badges() As List(Of Badge)
            Get
                Return _Badges
            End Get
        End Property

        ''' <summary>Destinations</summary>
        ''' <returns>List of Destinations</returns>
        Public ReadOnly Property Destinations() As List(Of Destination)
            Get
                Return _Destinations
            End Get
        End Property

        ''' <summary>Playlists</summary>
        ''' <returns>List of Playlists</returns>
        Public ReadOnly Property Playlists() As List(Of Playlist)
            Get
                Return _Playlists
            End Get
        End Property

        ''' <summary>Message</summary>
        ''' <value>Message as String</value>
        ''' <returns>System Message Text</returns>
        Public Property Message() As String
            Get
                Return strMessage
            End Get
            Set(ByVal Value As String)
                strMessage = Value
            End Set
        End Property

        ''' <summary>ErrorText</summary>
        ''' <value>ErrorText as String</value>
        ''' <returns>Error Text</returns>
        Public Property ErrorText() As String
            Get
                Return strError
            End Get
            Set(ByVal Value As String)
                strError = Value
            End Set
        End Property

        ''' <summary>ExceptionText</summary>
        ''' <value>ExceptionText as String</value>
        ''' <returns>Exception Text</returns>
        Public Property ExceptionText() As String
            Get
                Return strException
            End Get
            Set(ByVal Value As String)
                strException = Value
            End Set
        End Property

    End Class
End Namespace
